home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 …SCII & the Runetime Code / ADC Developer CD (1992-07) (''Butch ASCII And The Runtime Code'')_iso / Dev.CD 199207.iso / Developer Essentials / DTS Sample Code / Macintosh Sample Code / SC.011.GetZoneList / GetZoneList.p < prev    next >
Encoding:
Text File  |  1992-06-10  |  22.4 KB  |  820 lines  |  [TEXT/MPS ]

  1. {------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    AppleTalk GetZoneList Sample Application
  6. #
  7. #    GetZoneList
  8. #
  9. #    GetZoneList.p    -    Pascal Source
  10. #
  11. #    Copyright © 1988-90 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    1.00                November 1988
  15. #                1.01                October 1989
  16. #                1.02                May 1990
  17. #                1.03                June 1992
  18. #
  19. #    Components:    GetZoneList.c        May 1, 1990
  20. #                GetZoneList.p        May 1, 1990
  21. #                GetZoneList.r        May 1, 1990
  22. #                MakeFile            May 1, 1990
  23. #                UFailure.a            November 1, 1988
  24. #                UFailure.h            November 1, 1988
  25. #                UFailure.inc1.p        November 1, 1988
  26. #                UFailure.p            November 1, 1988
  27. #
  28. #    GetZoneList is a sample application that uses
  29. #    AppleTalk ATP and ZIP to obtain a list of zones
  30. #    on an AppleTalk internet.
  31. #
  32. #    GetZoneList also demonstrates using a signal, or
  33. #    failure-catching mechanism to recover from error
  34. #    situations.
  35. #
  36. #    GetZoneList is based on DTS Sample.p. For more
  37. #    description and explanantion of the non-example
  38. #    specific areas of this application, please refer to
  39. #    either Sample.p or TESample.p.
  40. #
  41. ------------------------------------------------------------------------------}
  42.  
  43. PROGRAM GetZoneList;
  44.  
  45. USES
  46.     MemTypes, QuickDraw, OSIntf, ToolIntf, AppleTalk, PackIntf, FixMath, Script, UFailure;
  47.  
  48. CONST
  49.     _WaitNextEvent            = $A860;
  50.     _Unimplemented            = $A89F;
  51.     kSysEnvironsVersion        = 1;
  52.     kOSEvent                = app4Evt;    {event used by MultiFinder}
  53.     kSuspendResumeMessage    = 1;        {high byte of suspend/resume event message}
  54.     kResumeMask                = 1;        {bit of message field for resume vs. suspend}
  55.  
  56.     kCR                = 13;                {carriage return character}
  57.     kENTER            = 3;                {enter character}
  58.     kScrollBarWidth    = 15;                {the width of the scrollbar in the list}
  59.     kListInset        = -1;                {adjustment for list frame}
  60.     kATPTimeOutVal    = 3;                {re-try ATP SendRequest every 3 seconds}
  61.     kATPRetryCount    = 5;                {for five times}
  62.     kZonesSize        = 578;                {size of buffer for zone names}
  63.     kGZLCall        = $08000000;        {GetZoneList indicator}
  64.     kZIPSocket        = 6;                {the Zone Information Protocol socket}
  65.     kMoreZones        = $FF000000;        {mask to see if more zones to come}
  66.     kZoneCount        = $0000FFFF;        {mask to count zones in buffer}
  67.     kHilite            = 1;                {hilite value for button control}
  68.     kDeHilite        = 0;                {dehilite value for button control}
  69.     kHiliteDelay    = 8;                {time in ticks to leave button hilited}
  70.  
  71.     kMinHeap        = (29 * 1024);
  72.     kMinSpace        = (20 * 1024);
  73.  
  74.     sErrStrings        = 128;                {error string STR#}
  75.     eStandardErr    = 1;
  76.     eWrongMachine    = 2;
  77.     eSmallSize        = 3;
  78.     eNoMemory        = 4;
  79.     eAppleTalk        = 5;
  80.     eNoZones        = 6;
  81.  
  82.     rAboutAlert        = 128;                {about alert}
  83.     rZoneDialog        = 129;                {zone list dialog}
  84.     dZoneList        = 2;                {user item that is zone list}
  85.     dDefault        = 3;                {user item that is default indicator}
  86.     rUserAlert        = 130;                {error alert}
  87.  
  88.     rMenuBar        = 128;                {application's menu bar}
  89.  
  90.     mApple            = 128;                {Apple menu}
  91.     iAbout            = 1;
  92.  
  93.     mFile            = 129;                {File menu}
  94.     iNew            = 1;
  95.     iClose            = 4;
  96.     iQuit            = 12;
  97.  
  98.     mEdit            = 130;                {Edit menu}
  99.     iUndo            = 1;
  100.     iCut            = 3;
  101.     iCopy            = 4;
  102.     iPaste            = 5;
  103.     iClear            = 6;
  104.  
  105.     {1.01 - kDITop and kDILeft are used to locate the Disk Initialization dialogs.}
  106.     kDITop        = $0050;
  107.     kDILeft        = $0070;
  108.  
  109. VAR
  110.     gMac                : SysEnvRec;    {set up by Initialize}
  111.     gHasWaitNextEvent    : BOOLEAN;        {set up by Initialize}
  112.     gInBackground        : BOOLEAN;        {maintained by Initialize and DoEvent}
  113.  
  114.     gList                : ListHandle;    {the list to be filled with zone names}
  115.  
  116.  
  117.  
  118. {$S Initialize}
  119. FUNCTION TrapAvailable(tNumber: INTEGER; tType: TrapType): BOOLEAN;
  120.  
  121. {Check to see if a given trap is implemented. This is only used by the
  122.  Initialize routine in this program, so we put it in the Initialize segment.
  123.  The recommended approach to see if a trap is implemented is to see if
  124.  the address of the trap routine is the same as the address of the
  125.  Unimplemented trap.}
  126. {1.02 - Needs to be called after call to SysEnvirons so that it can check
  127.  if a ToolTrap is out of range of a pre-MacII ROM.}
  128.  
  129. BEGIN
  130.     IF (tType = ToolTrap) &
  131.         (gMac.machineType > envMachUnknown) &
  132.         (gMac.machineType < envMacII) THEN BEGIN        {it's a 512KE, Plus, or SE}
  133.         tNumber := BAND(tNumber, $03FF);
  134.         IF tNumber > $01FF THEN                            {which means the tool traps}
  135.             tNumber := _Unimplemented;                    {only go to $01FF}
  136.     END;
  137.     TrapAvailable := NGetTrapAddress(tNumber, tType) <>
  138.                         GetTrapAddress(_Unimplemented);
  139. END; {TrapAvailable}
  140.  
  141.  
  142. {$S Main}
  143. PROCEDURE FailOSErrMsg(result, message: INTEGER);
  144. BEGIN
  145.     IF result <> noErr THEN
  146.         Failure(result, message);
  147. END; {SignalOSErrMsg}
  148.  
  149.  
  150. {$S Main}
  151. PROCEDURE FailNILMsg(p: UNIV Ptr; message: INTEGER);
  152. BEGIN
  153.     IF p = NIL THEN
  154.         Failure(memFullErr, message);
  155. END; {FailNILMsg}
  156.  
  157.  
  158. {$S Main}
  159. PROCEDURE AlertUser(error: INTEGER; message: LongInt);
  160.  
  161. {Display an alert to inform the user of an error. Message acts as an
  162.  index into a STR# resource of error messages. If no message is given,
  163.  i.e. = 0, then use a standard message. If error is not noErr then
  164.  display it as well.}
  165.  
  166. VAR
  167.     msg1, msg2    : Str255;
  168.     itemHit        : INTEGER;
  169. BEGIN
  170.     IF message <= 0 THEN message := eStandardErr;
  171.     GetIndString(msg1, sErrStrings, message);
  172.     IF error = noErr THEN
  173.         msg2 := ''
  174.     ELSE
  175.         NumToString(error, msg2);
  176.     ParamText(msg1, msg2, '', '');
  177.     itemHit := Alert(rUserAlert, NIL);
  178. END; {AlertUser}
  179.  
  180.  
  181. {$S Main}
  182. FUNCTION IsDAWindow(window: WindowPtr): BOOLEAN;
  183. BEGIN
  184.     IF window = NIL THEN
  185.         IsDAWindow := FALSE
  186.     ELSE    {DA windows have negative windowKinds}
  187.         IsDAWindow := WindowPeek(window)^.windowKind < 0;
  188. END; {IsDAWindow}
  189.  
  190.  
  191. {$S Main}
  192. FUNCTION IsAppWindow(window: WindowPtr): BOOLEAN;
  193. BEGIN
  194.     IF window = NIL THEN
  195.         IsAppWindow := FALSE
  196.     ELSE    {application windows have windowKinds >= userKind (8) or dialogKind (2)}
  197.         WITH WindowPeek(window)^ DO
  198.             IsAppWindow := (windowKind >= userKind) | (windowKind = dialogKind);
  199. END; {IsAppWindow}
  200.  
  201.  
  202. {$S Main}
  203. PROCEDURE BuildZoneList;
  204.  
  205. {Create the list of zones on the network. Find a bridge to talk to , if one is
  206.  present, then ask it for zone names. Add the names to the list in the dialog.}
  207.  
  208. VAR
  209.     dATPPBptr                    : ATPPBptr;        {the parameter block for GetZoneList call}
  210.     dBDS                        : BDSElement;    {the BDS for GetZoneList call}
  211.     dZones, dCurr                : Ptr;            {the data buffer for GetZoneList call}
  212.     dIndex, dCount, dNode, dNet    : INTEGER;
  213.     ignore                        : INTEGER;
  214.     cSize                        : Point;
  215.     fi                            : FailInfo;
  216.     nodeNetAddress, bridgeNode    : INTEGER;
  217.  
  218.     PROCEDURE CleanUp;
  219.     BEGIN
  220.         IF dATPPBptr <> NIL THEN
  221.             DisposPtr(Ptr(dATPPBptr));                    {get rid of pb block}
  222.         IF dZones <> NIL THEN
  223.             DisposPtr(dZones);                            {and buffer}
  224.     END; {CleanUp}
  225.  
  226.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  227.     BEGIN
  228.         CleanUp;                                        {get rid of allocated junk}
  229.     END;
  230.  
  231. BEGIN
  232.     dATPPBptr := NIL;                                    {init some important variables}
  233.     dZones := NIL;
  234.     CatchFailures(fi, HandleErr);
  235.  
  236.     { get network address of node & node ID of bridge (if any) }
  237.     FailOSErrMsg(GetNodeAddress(ignore, nodeNetAddress), eAppleTalk);
  238.     bridgeNode := GetBridgeAddress;
  239.  
  240.     { test to see if bridge node fails.  If so, no internet. }
  241.     if (bridgeNode = 0) then
  242.         Failure(0, eNoZones);                                    { bail if no zones present }
  243.  
  244.     dATPPBptr := ATPPBptr(NewPtr(SIZEOF(ATPParamBlock)));
  245.     FailNILMsg(dATPPBptr, eNoMemory);
  246.     dZones := NewPtr(kZonesSize);
  247.     FailNILMsg(dZones, eNoMemory);
  248.     WITH dBDS DO BEGIN                                    {set up BDS}
  249.         buffSize := kZonesSize;
  250.         buffPtr := dZones;
  251.         END;
  252.     WITH dATPPBptr^ DO BEGIN                            {set up pb block}
  253.         atpFlags := 0;
  254.  
  255.         addrBlock.aNet := nodeNetAddress;
  256.         addrBlock.aNode := bridgeNode;                    {get node of bridge}
  257.         addrBlock.aSocket := kZIPSocket;                {the socket we want}
  258.         reqLength := 0;
  259.         reqPointer := NIL;
  260.         bdsPointer := @dBDS;
  261.         numOfBuffs := 1;
  262.         timeOutVal := kATPTimeOutVal;
  263.         retryCount := kATPRetryCount;
  264.         END;
  265.     dIndex := 1;
  266.     dCount := 0;
  267.     SetPt(cSize, 0, 0);                                    {we always stuff into first}
  268.     REPEAT
  269.         dATPPBptr^.userData := kGZLCall + dIndex;        {indicate GetZoneList request}
  270.         FailOSErrMsg(PSendRequest(dATPPBptr,
  271.             FALSE), eAppleTalk);                        {send sync request}
  272.         dCount := dCount + BAND(dBDS.userBytes,
  273.                     kZoneCount);                        {find out how many returned}
  274.         dCurr := dZones;                                {put current pointer at start}
  275.         REPEAT                                            {get each zone}
  276.             ignore := LAddRow(1, 0, gList);                {create new cell at start}
  277.             LSetCell(POINTER(ORD4(dCurr) + 1), dCurr^,
  278.                         cSize, gList);                    {stuff in zone}
  279.             dCurr := POINTER(ORD4(dCurr) + dCurr^+1);    {bump up current pointer}
  280.             dIndex := dIndex + 1;                        {increment which zone}
  281.         UNTIL dIndex > dCount;
  282.     UNTIL (BAND(dBDS.userBytes, kMoreZones) <> 0);        {keep going until none left}
  283.     CleanUp;
  284.  
  285.     Success(fi);
  286. END; {BuildZoneList}
  287.  
  288.  
  289. {$S Main}
  290. PROCEDURE BuildZoneListPhase2;
  291.  
  292. {Create the list of zones on the network. Find a bridge to talk to , if one is
  293.  present, then ask it for zone names. Add the names to the list in the dialog.}
  294.  
  295. VAR
  296.     dXPBPBPtr                    : xPPParmBlkPtr;
  297.     dBDS                        : BDSElement;    {the BDS for GetZoneList call}
  298.     dZones, dCurr                : Ptr;            {the data buffer for GetZoneList call}
  299.     dIndex, dCount, dNode, dNet    : INTEGER;
  300.     ignore                        : INTEGER;
  301.     cSize                        : Point;
  302.     fi                            : FailInfo;
  303.     xppDriverRefNum                : INTEGER;
  304.     nodeNetAddress, bridgeNode    : INTEGER;
  305.  
  306.     PROCEDURE CleanUp;
  307.     BEGIN
  308.         IF dXPBPBPtr <> NIL THEN
  309.             DisposPtr(Ptr(dXPBPBPtr));                    {get rid of pb block}
  310.         IF dZones <> NIL THEN
  311.             DisposPtr(dZones);                            {and buffer}
  312.     END; {CleanUp}
  313.  
  314.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  315.     BEGIN
  316.         CleanUp;                                        {get rid of allocated junk}
  317.     END;
  318.  
  319. BEGIN
  320.     dXPBPBPtr := NIL;                                    {init some important variables}
  321.     dZones := NIL;
  322.     CatchFailures(fi, HandleErr);
  323.  
  324.     { get network address of node & node ID of bridge (if any) }
  325.     FailOSErrMsg(GetNodeAddress(ignore, nodeNetAddress), eAppleTalk);
  326.     bridgeNode := GetBridgeAddress;
  327.  
  328.     { test to see if either one fails.  If so, no internet. }
  329.     if (nodeNetAddress = 0) or (bridgeNode = 0) then
  330.         Failure(0, eNoZones);                                    { bail if no zones present }
  331.  
  332.     { get a hold of the XPP driver reference number-this is the safest way }
  333.     FailOSErrMsg(OpenDriver('.XPP', xppDriverRefNum), eAppleTalk);
  334.  
  335.     dXPBPBPtr := xPPParmBlkPtr(NewPtr(SIZEOF(xPPParamBlock)));
  336.     FailNILMsg(dXPBPBPtr, eNoMemory);
  337.     dZones := NewPtr(kZonesSize);
  338.     FailNILMsg(dZones, eNoMemory);
  339.  
  340.     dXPBPBPtr^.zipInfoField[1] := 0;    { ALWAYS 0 on first call.  has state info on subsequent calls }
  341.     dXPBPBPtr^.zipInfoField[2] := 0;    { ALWAYS 0 on first call.  has state info on subsequent calls }
  342.     dXPBPBPtr^.zipLastFlag := 0;
  343.  
  344.     dXPBPBPtr^.ioRefNum := xppDriverRefNum;
  345.     dXPBPBPtr^.csCode := xCall;
  346.     dXPBPBPtr^.xppSubCode := zipGetZoneList;
  347.     dXPBPBPtr^.xppTimeOut := kATPTimeOutVal;
  348.     dXPBPBPtr^.xppRetry := kATPRetryCount;
  349.     dXPBPBPtr^.zipBuffPtr := Ptr( dZones);
  350.  
  351.     dIndex := 1;
  352.     dCount := 0;
  353.     SetPt(cSize, 0, 0);                                    {we always stuff into first}
  354.     REPEAT
  355.         FailOSErrMsg(PBControl(ParmBlkPtr (dXPBPBPtr), false), eAppleTalk);        { send sync control call }
  356.         dCount := dCount + dXPBPBPtr^.zipNumZones;                        { find out how many returned }
  357.  
  358.         dCurr := dZones;                                {put current pointer at start}
  359.         REPEAT                                            {get each zone}
  360.             ignore := LAddRow(1, 0, gList);                {create new cell at start}
  361.             LSetCell(POINTER(ORD4(dCurr) + 1), dCurr^,
  362.                 cSize, gList);                            {stuff in zone}
  363.             dCurr := POINTER(ORD4(dCurr) + dCurr^+1);    {bump up current pointer}
  364.             dIndex := dIndex + 1;                        {increment which zone}
  365.         UNTIL dIndex > dCount;
  366.     UNTIL (dXPBPBPtr^.zipLastFlag <> 0);        {keep going until none left}
  367.     CleanUp;
  368.  
  369.     Success(fi);
  370. END; {BuildZoneListPhase2}
  371.  
  372.  
  373. {$S Main}
  374. PROCEDURE ZoneListDraw(dlg: DialogPtr; item: INTEGER);
  375.  
  376. {The user item procedure for the zone list user item and default
  377.  box user item in the dialog. Draw the list and the frame that goes with it.
  378.  Draw the default box around the OK button.}
  379.  
  380. VAR
  381.     port    : GrafPtr;
  382.     kind    : INTEGER;
  383.     h        : Handle;
  384.     r        : Rect;
  385.     ps        : PenState;
  386.  
  387. BEGIN
  388.     GetPort(port);                                    {save old port}
  389.     SetPort(dlg);                                    {make dialog port}
  390.     CASE item OF
  391.     dZoneList: BEGIN
  392.             LUpdate(dlg^.visRgn, gList);            {re-draw list}
  393.             GetDItem(dlg, dZoneList, kind, h, r);
  394.             InsetRect(r, kListInset, kListInset);
  395.             FrameRect(r);                            {re-draw frame}
  396.         END;
  397.     dDefault: BEGIN
  398.             GetDItem(dlg, dDefault, kind, h, r);
  399.             GetPenState(ps);
  400.             PenNormal;                                {always be on the defensive}
  401.             PenSize(3, 3);
  402.             InsetRect(r, -4, -4);
  403.             FrameRoundRect(r, 16, 16);                {draw default box}
  404.             SetPenState(ps);
  405.         END;
  406.     END;
  407.     SetPort(port);                                    {restore old port}
  408. END; {ZoneListDraw}
  409.  
  410.  
  411. {$S Main}
  412. FUNCTION ListFilter (dlg: DialogPtr; VAR event: EventRecord;
  413.                                             VAR item: INTEGER) : BOOLEAN;
  414.  
  415. {Passed as parameter to ModalDialog. Handle key presses and mouse clicks
  416.  from the user. Do all the right default actions since we override them
  417.  by virtue of our existence.}
  418.  
  419. VAR
  420.     port        : GrafPtr;
  421.     loc            : Point;
  422.     kind        : INTEGER;
  423.     h            : Handle;
  424.     r            : Rect;
  425.     ignore        : BOOLEAN;
  426.     key            : SignedByte;
  427.     finalTicks    : LongInt;
  428. BEGIN
  429.     ListFilter := FALSE;                                {always default FALSE}
  430.     CASE event.what OF
  431.         keyDown, autoKey: BEGIN                            {check for <cr> or <enter>}
  432.             key := SignedByte(event.message);
  433.             IF key IN [kCR, kENTER] THEN BEGIN            {it was a <cr> or <enter>}
  434.                 GetDItem(dlg, ok, kind, h, r);
  435.                 HiliteControl(ControlHandle(h), kHilite);
  436.                 Delay(kHiliteDelay, finalTicks);
  437.                 HiliteControl(ControlHandle(h), kDeHilite);
  438.                 ListFilter := TRUE;                        {so we handle it}
  439.                 item := 1;                                {and make the first item hit}
  440.             END;
  441.         END;
  442.         mouseDown: BEGIN                                {we want mouseDowns}
  443.             GetPort(port);
  444.             SetPort(dlg);
  445.             loc := event.where;
  446.             GlobalToLocal(loc);                            {find where clicked}
  447.             GetDItem(dlg, dZoneList, kind, h, r);        {get rect for list}
  448.             IF PtInRect(loc, r) THEN BEGIN                {if clicked inside…}
  449.                 ListFilter := TRUE;                        {we take care of it}
  450.                 ignore := LClick(loc, event.modifiers,
  451.                                     gList);                {by passing click to list}
  452.             END;
  453.             SetPort(port);
  454.         END;
  455.     END;
  456. END; {ListFilter}
  457.  
  458.  
  459. {$S Main}
  460. PROCEDURE DoZoneList;
  461.  
  462. {Put up a modal dialog that shows a list of the zones on the net. Create the dialog
  463.  and list, call BuildZoneList to fill it, then wait for the user to click OK.}
  464.  
  465. VAR
  466.     dlg                        : DialogPtr;
  467.     item, kind                : INTEGER;
  468.     h                        : Handle;
  469.     r, rView, dataBounds    : Rect;
  470.     cSize                    : Point;
  471.     fi                        : FailInfo;
  472.     hor, ver                : INTEGER;
  473.  
  474.     PROCEDURE CleanUp;
  475.     BEGIN
  476.         IF gList <> NIL THEN
  477.             LDispose(gList);                                {get rid of list}
  478.         IF dlg <> NIL THEN
  479.             DisposDialog(dlg);                                {get rid of dialog}
  480.     END; {CleanUp}
  481.  
  482.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  483.     BEGIN
  484.         CleanUp;                                            {release junk}
  485.     END;
  486.  
  487. BEGIN
  488.     gList := NIL;                                            {init some important variables}
  489.     dlg := NIL;
  490.     CatchFailures(fi, HandleErr);
  491.  
  492.     dlg := GetNewDialog(rZoneDialog, NIL, POINTER(-1));        {create dialog}
  493.     FailNILMsg(dlg, eNoMemory);
  494.  
  495.     {We center the dialog horizontally and position it vertically one-third the
  496.      distance from the menu bar to the bottom of the main device. We do not
  497.      check for the dialog extending past the bottom of the device because we
  498.      know the dialog is not that big. You may wish to make that check.}
  499.     WITH dlg^.portRect DO BEGIN
  500.         hor := right - left;
  501.         ver := bottom - top;
  502.     END;
  503.     WITH screenBits.bounds DO BEGIN
  504.         hor := ((right - left) - hor) DIV 2;
  505.         ver := (((bottom - top) - ver - GetMBarHeight) DIV 3) + GetMBarHeight;
  506.     END;
  507.     MoveWindow(dlg, hor, ver, FALSE);
  508.  
  509.     GetDItem(dlg, dDefault, kind, h, r);
  510.     h := @ZoneListDraw;                                        {connect drawing procedure}
  511.     SetDItem(dlg, dDefault, kind, h, r);
  512.     GetDItem(dlg, dZoneList, kind, h, r);
  513.     h := @ZoneListDraw;                                        {connect drawing procedure}
  514.     SetDItem(dlg, dZoneList, kind, h, r);
  515.     rView := r;
  516.     WITH rView DO
  517.         right := right - kScrollBarWidth;                    {adjust rectangle for scroll}
  518.     SetRect(dataBounds, 0, 0, 1, 0);                        {init to one-wide list}
  519.     SetPt(cSize, 0, 0);
  520.     gList := LNew(rView, dataBounds, cSize, 0, WindowPtr(dlg),
  521.                     FALSE, FALSE, FALSE, TRUE);                {create with vertical scroll}
  522.     FailNILMsg(gList, eNoMemory);
  523.  
  524.     if gMac.atDrvrVersNum >= 53 then
  525.         BuildZoneListPhase2
  526.     else
  527.         BuildZoneList;                                            {put the stuff into the list}
  528.  
  529.     SetPt(cSize, 0, 0);
  530.     LSetSelect(TRUE, cSize, gList);                            {select the first guy}
  531.     LDoDraw(TRUE, gList);                                    {turn on the list}
  532.     ShowWindow(dlg);                                        {turn on the dialog}
  533.     REPEAT
  534.         ModalDialog(@ListFilter, item);                        {accept events}
  535.     UNTIL item = ok;                                        {until he presses 'ok'}
  536.     CleanUp;
  537.  
  538.     Success(fi);
  539. END; {DoZoneList}
  540.  
  541.  
  542. {$S Main}
  543. FUNCTION DoCloseWindow(window: WindowPtr) : BOOLEAN;
  544. BEGIN
  545.     DoCloseWindow := TRUE;
  546.     IF IsDAWindow(window) THEN
  547.         CloseDeskAcc(WindowPeek(window)^.windowKind);
  548.     IF IsAppWindow(window) THEN
  549.         CloseWindow(window);
  550. END; {DoCloseWindow}
  551.  
  552.  
  553. {$S Initialize}
  554. PROCEDURE Initialize;
  555. VAR
  556.     menuBar            : Handle;
  557.     window            : WindowPtr;
  558.     ignoreError        : OSErr;
  559.     total, contig    : LongInt;
  560.     ignoreResult    : BOOLEAN;
  561.     event            : EventRecord;
  562.     count            : INTEGER;
  563.     fi                : FailInfo;
  564.  
  565.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  566.     BEGIN
  567.         IF error > 0 THEN
  568.             AlertUser(0, error)
  569.         ELSE
  570.             AlertUser(error, message);
  571.         ExitToShell;
  572.     END; {HandleErr}
  573.  
  574. BEGIN
  575.     gHasWaitNextEvent := TrapAvailable(_WaitNextEvent, ToolTrap);
  576.     gInBackground := FALSE;
  577.  
  578.     InitGraf(@thePort);
  579.     InitFonts;
  580.     InitWindows;
  581.     InitMenus;
  582.     TEInit;
  583.     InitDialogs(NIL);
  584.     InitCursor;
  585.  
  586.     FOR count := 1 TO 3 DO
  587.         ignoreResult := EventAvail(everyEvent, event);
  588.  
  589.     CatchFailures(fi, HandleErr);
  590.  
  591.     FailOSErrMsg(MPPOpen, eAppleTalk);
  592.     FailOSErrMsg(ATPLoad, eAppleTalk);
  593.  
  594.     ignoreError := SysEnvirons(kSysEnvironsVersion, gMac);
  595.     IF gMac.machineType < 0 THEN
  596.         Failure(0, eWrongMachine);
  597.  
  598.     IF ORD(GetApplLimit) - ORD(ApplicZone) < kMinHeap THEN
  599.         Failure(0, eSmallSize);
  600.  
  601.     PurgeSpace(total, contig);
  602.     IF total < kMinSpace THEN
  603.         Failure(0, eNoMemory);
  604.  
  605.     menuBar := GetNewMBar(rMenuBar);        {read menus into menu bar}
  606.     FailNILMsg(menuBar, eNoMemory);
  607.     SetMenuBar(menuBar);                    {install menus}
  608.     DisposHandle(menuBar);
  609.     AddResMenu(GetMHandle(mApple), 'DRVR');    {add DA names to Apple menu}
  610.     DrawMenuBar;
  611.  
  612.     Success(fi);
  613. END; {Initialize}
  614.  
  615.  
  616. {$S Main}
  617. PROCEDURE Terminate;
  618. VAR
  619.     aWindow    : WindowPtr;
  620.     closed    : BOOLEAN;
  621.  
  622. BEGIN
  623.     closed := TRUE;
  624.     REPEAT
  625.         aWindow := FrontWindow;                    {get the current front window}
  626.         IF aWindow <> NIL THEN
  627.             closed := DoCloseWindow(aWindow);    {close this window}
  628.     UNTIL (NOT closed) | (aWindow = NIL);        {do all windows}
  629.     IF closed THEN
  630.         ExitToShell;                            {exit if no cancellation}
  631. END; {Terminate}
  632.  
  633.  
  634. {$S Main}
  635. PROCEDURE AdjustMenus;
  636. VAR
  637.     window            : WindowPtr;
  638.     menu            : MenuHandle;
  639.  
  640. BEGIN
  641.     window := FrontWindow;
  642.  
  643.     menu := GetMHandle(mFile);
  644.     IF IsDAWindow(window) THEN                {we can allow desk accessories to be closed from the menu}
  645.         EnableItem(menu, iClose)
  646.     ELSE
  647.         DisableItem(menu, iClose);            {but not our traffic light window}
  648.  
  649.     menu := GetMHandle(mEdit);
  650.     IF IsDAWindow(window) THEN BEGIN        {a desk accessory might need the edit menu}
  651.         EnableItem(menu, iUndo);
  652.         EnableItem(menu, iCut);
  653.         EnableItem(menu, iCopy);
  654.         EnableItem(menu, iPaste);
  655.         EnableItem(menu, iClear);
  656.     END ELSE BEGIN                            {but we know we do not}
  657.         DisableItem(menu, iUndo);
  658.         DisableItem(menu, iCut);
  659.         DisableItem(menu, iCopy);
  660.         DisableItem(menu, iClear);
  661.         DisableItem(menu, iPaste);
  662.     END;
  663.  
  664. END; {AdjustMenus}
  665.  
  666.  
  667. {$S Main}
  668. PROCEDURE DoMenuCommand(menuResult: LONGINT);
  669. VAR
  670.     menuID            : INTEGER;        {the resource ID of the selected menu}
  671.     menuItem        : INTEGER;        {the item number of the selected menu}
  672.     itemHit            : INTEGER;
  673.     daName            : Str255;
  674.     daRefNum        : INTEGER;
  675.     handledByDA        : BOOLEAN;
  676.     ignore            : BOOLEAN;
  677.     fi                : FailInfo;
  678.  
  679.     PROCEDURE HandleMenu(error: INTEGER; message: LongInt);
  680.     BEGIN
  681.         HiliteMenu(0);                {unhighlight what MenuSelect (or MenuKey) hilited}
  682.     END;
  683.  
  684. BEGIN
  685.     CatchFailures(fi, HandleMenu);
  686.     menuID := HiWrd(menuResult);    {use built-ins (for efficiency)...}
  687.     menuItem := LoWrd(menuResult);    {to get menu item number and menu number}
  688.     CASE menuID OF
  689.         mApple:
  690.             CASE menuItem OF
  691.                 iAbout:                {bring up alert for About}
  692.                     itemHit := Alert(rAboutAlert, NIL);
  693.                 OTHERWISE BEGIN        {all non-About items in this menu are DAs}
  694.                     GetItem(GetMHandle(mApple), menuItem, daName);
  695.                     daRefNum := OpenDeskAcc(daName);
  696.                 END;
  697.             END;
  698.         mFile:
  699.             CASE menuItem OF
  700.                 iNew:
  701.                     DoZoneList;
  702.                 iClose:
  703.                     ignore := DoCloseWindow(FrontWindow);
  704.                 iQuit:
  705.                     Terminate;
  706.             END;
  707.         mEdit:                        {call SystemEdit for DA editing & MultiFinder}
  708.             handledByDA := SystemEdit(menuItem-1);    {since we don't do any editing}
  709.     END;
  710.     Success(fi);
  711.     HiliteMenu(0);                    {cleanup}
  712. END; {DoMenuCommand}
  713.  
  714.  
  715. {$S Main}
  716. PROCEDURE DoEvent(event: EventRecord);
  717. VAR
  718.     part, err    : INTEGER;
  719.     window        : WindowPtr;
  720.     hit            : BOOLEAN;
  721.     key            : CHAR;
  722.     fi            : FailInfo;
  723.     aPoint        : Point;
  724.  
  725.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  726.     BEGIN
  727.         IF error > 0 THEN
  728.             AlertUser(0, error)
  729.         ELSE
  730.             AlertUser(error, message);
  731.         EXIT(DoEvent);
  732.     END; {HandleErr}
  733.  
  734. BEGIN
  735.     CatchFailures(fi, HandleErr);
  736.  
  737.     CASE event.what OF
  738.         mouseDown: BEGIN
  739.             part := FindWindow(event.where, window);
  740.             CASE part OF
  741.                 inMenuBar: BEGIN            {process the menu command}
  742.                     AdjustMenus;
  743.                     DoMenuCommand(MenuSelect(event.where));
  744.                 END;
  745.                 inSysWindow:                {let the system handle the mouseDown}
  746.                     SystemClick(event, window);
  747.                 inContent:;
  748.                 inDrag:;
  749.                 inGrow:;
  750.                 inZoomIn, inZoomOut:;
  751.             END;
  752.         END;
  753.         keyDown, autoKey: BEGIN                {check for menukey equivalents}
  754.             key := CHR(BAnd(event.message, charCodeMask));
  755.             IF BAnd(event.modifiers, cmdKey) <> 0 THEN    {Command key down}
  756.                 IF event.what = keyDown THEN BEGIN
  757.                     AdjustMenus;            {enable/disable/check menu items properly}
  758.                     DoMenuCommand(MenuKey(key));
  759.                 END;
  760.         END;                                {call DoActivate with the window and...}
  761.         activateEvt:;
  762.         updateEvt:;
  763.         {1.01 - It is not a bad idea to at least call DIBadMount in response
  764.          to a diskEvt, so that the user can format a floppy.}
  765.         diskEvt:
  766.             IF HiWrd(event.message) <> noErr THEN BEGIN
  767.                 SetPt(aPoint, kDILeft, kDITop);
  768.                 err := DIBadMount(aPoint, event.message);
  769.             END;
  770.         kOSEvent:
  771.             CASE BAnd(BRotL(event.message, 8), 255) OF    {high byte of message}
  772.                 kSuspendResumeMessage: BEGIN
  773.                     gInBackground := BAnd(event.message, kResumeMask) = 0;
  774.                 END;
  775.             END;
  776.     END;
  777.  
  778.     Success(fi);
  779. END; {DoEvent}
  780.  
  781.  
  782. {$S Main}
  783. PROCEDURE EventLoop;
  784. VAR
  785.     cursorRgn    : RgnHandle;
  786.     gotEvent    : BOOLEAN;
  787.     event        : EventRecord;
  788.  
  789. BEGIN
  790.     cursorRgn := NewRgn;            {we’ll pass WNE an empty region the 1st time thru}
  791.  
  792.     REPEAT
  793.         IF gHasWaitNextEvent THEN    {put us 'asleep' forever under MultiFinder}
  794.             gotEvent := WaitNextEvent(everyEvent, event, MAXLONGINT, cursorRgn)
  795.         ELSE BEGIN
  796.             SystemTask;                {must be called if using GetNextEvent}
  797.             gotEvent := GetNextEvent(everyEvent, event);
  798.         END;
  799.         IF gotEvent THEN BEGIN
  800.             DoEvent(event);
  801.         END;
  802.     UNTIL FALSE;                    {loop forever; we quit through an ExitToShell}
  803. END; {EventLoop}
  804.  
  805.  
  806. PROCEDURE _DataInit; EXTERNAL;
  807.  
  808.  
  809. {$S Main}
  810. BEGIN
  811.     UnloadSeg(@_DataInit);        {note that _DataInit must not be in Main!}
  812.     MaxApplZone;                {expand the heap so code segments load at the top}
  813.  
  814.     InitSignals;
  815.     Initialize;                    {initialize the program}
  816.     UnloadSeg(@Initialize);        {note that Initialize must not be in Main!}
  817.  
  818.     EventLoop;                    {call the main event loop}
  819. END.
  820.